This group assignment provides practice in foreign exchange markets as well as R models of those markets. Specifically we will practice reading in data, exploring time series, estimating auto and cross correlations, and investigating volatility clustering in financial time series. We will summarize our experiences in debrief. We will pay special attention to the financial economics of exchange rates.
In this set we will build and explore a data set using filters and if and diff statements. We will then answer some questions using plots and a pivot table report. We will then review a function to house our approach in case we would like to run some of the same analysis on other data sets.
Marketing and accounts receivables managers at our company continue to note we have a significant exposure to exchange rates. Our functional currency (what we report in financial statements) is in U.S. dollars (USD).
Our customer base is located in the United Kingdom, across the European Union, and in Japan. The exposure hits the gross revenue line of our financials.
Cash flow is further affected by the ebb and flow of accounts receivable components of working capital in producing and selling several products. When exchange rates are volatile, so is earnings, and more importantly, our cash flow.
Our company has also missed earnings forecasts for five straight quarters.
To get a handle on exchange rate exposures we download this data set and review some basic aspects of the exchange rates.
# Read in data
#Setting the working directory
# Ali & Windows WD
# setwd("C:\\Users\\ho511\\Desktop\\FIN 654\\Project 2")
# Kendra WD
setwd("/Users/kendraosburn/syracuse/FIN_654/data")
#According to R-bloggers, the zoo package performs calculations on irregular time series data. The zoo package can communicate with all other CRAN time series packages. Therefore, aiding in the ability to pass the time series objects between all time series packages. It does not have any modeling functionalities. https://www.r-bloggers.com/zoo-time-series-exercises/
#https://www.rdocumentation.org/packages/zoo/versions/1.8-6
library(zoo)
#Another time series package that allows for uniformity in handling time series data. XTS has functions to apply a function over different calendar periods, calculate products by period, as well as many more.
library(xts)
#ggplot2 is a package written by Hadley Wickham and creates beautiful graphics. It is part of tidyverse. ggplot requires the following elements: the data, the aesthetics and the geometry (type of plot). ggplot can take many many more elements, but the three listed are the most basic elements that are required to make ggplot work. For more information: https://www.rstudio.com/wp-content/uploads/2015/03/ggplot2-cheatsheet.pdf
library(ggplot2)
# Read and review a csv file from FRED
#We are reading in our csv file exrates, header = TRUE because the first line of the csv file has column names. Finally, na.omit, removes all rows with missing data. We are saving the data as a df named exrates.
exrates <- na.omit(read.csv("exrates.csv", header = TRUE))
# Check the data
#head() allows us to look at the first 6 rows of our df. We can see that our df has 5 variables: Date, USD.EUR, USD.GBP, USD.CNY, USD.JPY. The first date in our df is 1/29/2012. It look like our data was collected weekly.
head(exrates)
## DATE USD.EUR USD.GBP USD.CNY USD.JPY
## 1 1/29/2012 0.763678 0.638932 6.29509 77.1840
## 2 2/5/2012 0.760684 0.633509 6.29429 76.3930
## 3 2/12/2012 0.757491 0.632759 6.29232 77.2049
## 4 2/19/2012 0.760889 0.634166 6.29644 78.7109
## 5 2/26/2012 0.750301 0.632641 6.29710 80.3373
## 6 3/4/2012 0.750474 0.629771 6.29873 81.1607
#tail() allows us to look at the last 6 rows of the df. The last date in our df is 1/15/2017. At first glance, you can see that between the first 6 dates and the last 6 dates of our data, all of our values have increased. The largest average percentage increase is in the USD.JPY column, followed by USD.EUR and USD.GBP which, showed a similar percentage increase. The smallest percentage increase is USD.CNY.
tail(exrates)
## DATE USD.EUR USD.GBP USD.CNY USD.JPY
## 255 12/11/2016 0.938872 0.791554 6.93141 114.397
## 256 12/18/2016 0.950478 0.796572 6.93042 116.796
## 257 12/25/2016 0.958288 0.810481 6.94908 117.469
## 258 1/1/2017 0.954067 0.813594 6.94929 117.100
## 259 1/8/2017 0.951493 0.812388 6.92820 116.968
## 260 1/15/2017 0.943352 0.820854 6.91781 115.287
#str shows the structure of the data. In this case, our df has 260 observations (rows) of 5 variables. Date is currently stored as a factor, we should change this to the date data type. The other 4 variables are stored as data type numeric.
str(exrates)
## 'data.frame': 260 obs. of 5 variables:
## $ DATE : Factor w/ 260 levels "1/1/2017","1/10/2016",..: 15 103 89 94 100 126 109 114 120 130 ...
## $ USD.EUR: num 0.764 0.761 0.757 0.761 0.75 ...
## $ USD.GBP: num 0.639 0.634 0.633 0.634 0.633 ...
## $ USD.CNY: num 6.3 6.29 6.29 6.3 6.3 ...
## $ USD.JPY: num 77.2 76.4 77.2 78.7 80.3 ...
# Begin to explore the data
#summary() function gives summary statistics for each variable (column). It calculates the minimum and maximum or each column, the 1st quartile (where 25% of the data falls below), the median (where 50% of the data falls below and 50% is above) and the 3rd quartile (where 75% of the data falls below). It also calculates the mean, the average for each column.
summary(exrates)
## DATE USD.EUR USD.GBP USD.CNY
## 1/1/2017 : 1 Min. :0.7199 Min. :0.5835 Min. :6.092
## 1/10/2016: 1 1st Qu.:0.7544 1st Qu.:0.6224 1st Qu.:6.149
## 1/11/2015: 1 Median :0.7926 Median :0.6418 Median :6.279
## 1/12/2014: 1 Mean :0.8196 Mean :0.6561 Mean :6.310
## 1/13/2013: 1 3rd Qu.:0.8932 3rd Qu.:0.6656 3rd Qu.:6.369
## 1/15/2017: 1 Max. :0.9583 Max. :0.8209 Max. :6.949
## (Other) :254
## USD.JPY
## Min. : 76.39
## 1st Qu.: 96.90
## Median :102.44
## Mean :103.05
## 3rd Qu.:117.19
## Max. :124.78
##
We will calculate percentage changes as log returns of currency pairs. Our interest is in the ups and downs. To look at that we use if and else statements to define a new column called direction. We will build a data frame to house this initial analysis.
Using this data frame, interpret appreciation and depreciation in terms of the impact on the receipt of cash flow from customer’s accounts that are denominated in other than our USD functional currency.
# Compute log differences percent using as.matrix to force numeric type
##returns suitably lagged and iterated differences of the log of the exchange rates as as forced numeric type
#The following code removes the first column (dates) of the exrates df and then creates a matrix and calculates the natural logarthim for all entries in the exrates df. Then the diff function is calculating the differences between the consecutive log values. Lastly, the values are multiplied by 100 and are saved as a numeric data type in the matrix. Since we are calculating the difference between consecutive rows, the matrix will contain 1 less entry than our exrates df.
exrates.r <- diff(log(as.matrix(exrates[, -1]))) * 100
#head() gives the first 6 rows of the exrates.r matrix
head(exrates.r)
## USD.EUR USD.GBP USD.CNY USD.JPY
## 2 -0.39282058 -0.8523826 -0.01270912 -1.0301113
## 3 -0.42063724 -0.1184583 -0.03130311 1.0571858
## 4 0.44758304 0.2221127 0.06545522 1.9318720
## 5 -1.40130272 -0.2407629 0.01048156 2.0452375
## 6 0.02305476 -0.4546859 0.02588158 1.0197119
## 7 1.19869144 0.7988383 0.22598055 0.6384155
#tail() gives the last 6 rows of the exrates.r matrix
tail(exrates.r)
## USD.EUR USD.GBP USD.CNY USD.JPY
## 255 -0.1234763 -0.4555311 0.602265010 0.9803397
## 256 1.2285861 0.6319419 -0.014283828 2.0753968
## 257 0.8183343 1.7310378 0.268885929 0.5745646
## 258 -0.4414460 0.3833571 0.003021937 -0.3146198
## 259 -0.2701570 -0.1483412 -0.303945688 -0.1127877
## 260 -0.8592840 1.0367203 -0.150079365 -1.4475722
#str() shows the structure of our data. It shows that there is a numeric matrix with 259 rows and 4 columns.
str(exrates.r)
## num [1:259, 1:4] -0.3928 -0.4206 0.4476 -1.4013 0.0231 ...
## - attr(*, "dimnames")=List of 2
## ..$ : chr [1:259] "2" "3" "4" "5" ...
## ..$ : chr [1:4] "USD.EUR" "USD.GBP" "USD.CNY" "USD.JPY"
# Create size and direction
##This stores the absolute value of exchange rates and omits missing values
#The following line is calculating the absolute value of the entries in the exrates.r matrix and removes any rows with missing values. The result is stored in a matrix called size.
size <- na.omit(abs(exrates.r)) # size is indicator of volatility
#head() gives us the first 6 rows of the size matrix
head(size)
## USD.EUR USD.GBP USD.CNY USD.JPY
## 2 0.39282058 0.8523826 0.01270912 1.0301113
## 3 0.42063724 0.1184583 0.03130311 1.0571858
## 4 0.44758304 0.2221127 0.06545522 1.9318720
## 5 1.40130272 0.2407629 0.01048156 2.0452375
## 6 0.02305476 0.4546859 0.02588158 1.0197119
## 7 1.19869144 0.7988383 0.22598055 0.6384155
#This code changes the column names of size to the originalcolumnname.size, sep = "" shows that the original column name and .size are not separated by anything.However, we do not need this step, because when we combine all the matrices to a df the matrix name will be included in the column name.
#colnames(size) <- paste(colnames(size),".size", sep = "") # Teetor
##returns 1 if the exchange rate is positive, -1 if negative, or 0 if 0
#This creates a matrix composed of 1 and -1, depending on if the differential log rates were negative or positive. if the rate is greater than 0 then it will be replaced with a 1, if the rate is less than 0 than it will be replaced with a -1 and if the rate = 0, then it will stay 0 indicating no change. 1 indicates an increase and -1 indicates a decrease. The new matrix is named direction.
direction <- ifelse(exrates.r > 0, 1, ifelse(exrates.r < 0, -1, 0)) # another indicator of volatility
#This code changes the column names of direction to the originalcolumnname.dir, sep = "" shows that the original column name and .dir are not separated by anything.
#colnames(direction) <- paste(colnames(direction),".dir", sep = "")
#head() displays the first 6 rows of the direction matrix. We can see that it successfully changed the entries to 1, -1, and potentially 0.
head(direction)
## USD.EUR USD.GBP USD.CNY USD.JPY
## 2 -1 -1 -1 -1
## 3 -1 -1 -1 1
## 4 1 1 1 1
## 5 -1 -1 1 1
## 6 1 -1 1 1
## 7 1 1 1 1
# Convert into a time series object:
# 1. Split into date and rates
#as.Date() is changing the data type of our variable Dates in the exrates df. It is also removing the first entry from the date variable in the exrates df. Finally, it is storing all the dates, except for the first entry, in a vector called dates.
dates <- as.Date(exrates$DATE[-1], "%m/%d/%Y")
##combines the vlues of each item in the function as the variable "values"
#cbind() stands for column bind. In this case we are combining the exrates.r matrix, the size matrix and the direction matrix together into one matrix named values.
values <- cbind(exrates.r, size, direction)
#head() displays the first 6 rows of our new matrix.
head(values)
## USD.EUR USD.GBP USD.CNY USD.JPY USD.EUR USD.GBP
## 2 -0.39282058 -0.8523826 -0.01270912 -1.0301113 0.39282058 0.8523826
## 3 -0.42063724 -0.1184583 -0.03130311 1.0571858 0.42063724 0.1184583
## 4 0.44758304 0.2221127 0.06545522 1.9318720 0.44758304 0.2221127
## 5 -1.40130272 -0.2407629 0.01048156 2.0452375 1.40130272 0.2407629
## 6 0.02305476 -0.4546859 0.02588158 1.0197119 0.02305476 0.4546859
## 7 1.19869144 0.7988383 0.22598055 0.6384155 1.19869144 0.7988383
## USD.CNY USD.JPY USD.EUR USD.GBP USD.CNY USD.JPY
## 2 0.01270912 1.0301113 -1 -1 -1 -1
## 3 0.03130311 1.0571858 -1 -1 -1 1
## 4 0.06545522 1.9318720 1 1 1 1
## 5 0.01048156 2.0452375 -1 -1 1 1
## 6 0.02588158 1.0197119 1 -1 1 1
## 7 0.22598055 0.6384155 1 1 1 1
# for dplyr pivoting we need a data frame
##For dataframe the arguments set with a name change for exrates.r to returns
#We are creating a data frame using the 4 matrices previously created. dates =, returns =, size =, and direction = names the columns for the df. Since there are multiple columns in each matrix, the column name will be added to the original column name in the matrix. For example, USD.EUR will turn into returns.USD.EUR...
exrates.df <- data.frame(dates = dates, returns = exrates.r, size = size, direction = direction)
#str() gives the structure of each variable in the df. The df has 259 observations and 13 variables. Dates is saved as date, and the other 12 variables are numeric.
str(exrates.df) # notice the returns.* and direction.* prefixes
## 'data.frame': 259 obs. of 13 variables:
## $ dates : Date, format: "2012-02-05" "2012-02-12" ...
## $ returns.USD.EUR : num -0.3928 -0.4206 0.4476 -1.4013 0.0231 ...
## $ returns.USD.GBP : num -0.852 -0.118 0.222 -0.241 -0.455 ...
## $ returns.USD.CNY : num -0.0127 -0.0313 0.0655 0.0105 0.0259 ...
## $ returns.USD.JPY : num -1.03 1.06 1.93 2.05 1.02 ...
## $ size.USD.EUR : num 0.3928 0.4206 0.4476 1.4013 0.0231 ...
## $ size.USD.GBP : num 0.852 0.118 0.222 0.241 0.455 ...
## $ size.USD.CNY : num 0.0127 0.0313 0.0655 0.0105 0.0259 ...
## $ size.USD.JPY : num 1.03 1.06 1.93 2.05 1.02 ...
## $ direction.USD.EUR: num -1 -1 1 -1 1 1 1 -1 -1 1 ...
## $ direction.USD.GBP: num -1 -1 1 -1 -1 1 1 -1 -1 1 ...
## $ direction.USD.CNY: num -1 -1 1 1 1 1 1 -1 -1 -1 ...
## $ direction.USD.JPY: num -1 1 1 1 1 1 1 -1 -1 -1 ...
# 2. Make an xts object with row names equal to the dates
##This sets the values as an Extensible Time Series
#as.xts() is a function from the xts package. It creates xts objects from time series, ts, irts, fts, matrix, data frames and zoo objects. In this case, we are creating a xts object from the values matrix and it appears that we are adding the dates column as row names of the xts series. We are also removing any rows that contain missing values. This is saved as a xts object named exrates.xts
exrates.xts <- na.omit(as.xts(values, dates)) #order.by=as.Date(dates, "%d/%m/%Y")))
#str() gives us the str of exrates.xts. It shows that it is an xts object on the first/last data containing 259 rows and 12 columns. The objects are indexed by Date.
head(exrates.xts)
## USD.EUR USD.GBP USD.CNY USD.JPY USD.EUR
## 2012-02-05 -0.39282058 -0.8523826 -0.01270912 -1.0301113 0.39282058
## 2012-02-12 -0.42063724 -0.1184583 -0.03130311 1.0571858 0.42063724
## 2012-02-19 0.44758304 0.2221127 0.06545522 1.9318720 0.44758304
## 2012-02-26 -1.40130272 -0.2407629 0.01048156 2.0452375 1.40130272
## 2012-03-04 0.02305476 -0.4546859 0.02588158 1.0197119 0.02305476
## 2012-03-11 1.19869144 0.7988383 0.22598055 0.6384155 1.19869144
## USD.GBP USD.CNY USD.JPY USD.EUR USD.GBP USD.CNY USD.JPY
## 2012-02-05 0.8523826 0.01270912 1.0301113 -1 -1 -1 -1
## 2012-02-12 0.1184583 0.03130311 1.0571858 -1 -1 -1 1
## 2012-02-19 0.2221127 0.06545522 1.9318720 1 1 1 1
## 2012-02-26 0.2407629 0.01048156 2.0452375 -1 -1 1 1
## 2012-03-04 0.4546859 0.02588158 1.0197119 1 -1 1 1
## 2012-03-11 0.7988383 0.22598055 0.6384155 1 1 1 1
str(exrates.xts)
## An 'xts' object on 2012-02-05/2017-01-15 containing:
## Data: num [1:259, 1:12] -0.3928 -0.4206 0.4476 -1.4013 0.0231 ...
## - attr(*, "dimnames")=List of 2
## ..$ : NULL
## ..$ : chr [1:12] "USD.EUR" "USD.GBP" "USD.CNY" "USD.JPY" ...
## Indexed by objects of class: [Date] TZ: UTC
## xts Attributes:
## NULL
#as.zooreg() is part of the zoo package. It creates a regular zoo object, which is indexed by ordered observations. Similar to a ts class. From: https://www.rdocumentation.org/packages/zoo/versions/1.8-6/topics/zooreg. na.omit() removes any rows that have missing values.
exrates.zr <- na.omit(as.zooreg(exrates.xts))
#Getting the structure of exrates.zr. It is a zooreg series from 2012/02/05 to 2017/01/15 and is compiled of numeric data
str(exrates.zr)
## 'zooreg' series from 2012-02-05 to 2017-01-15
## Data: num [1:259, 1:12] -0.3928 -0.4206 0.4476 -1.4013 0.0231 ...
## - attr(*, "dimnames")=List of 2
## ..$ : NULL
## ..$ : chr [1:12] "USD.EUR" "USD.GBP" "USD.CNY" "USD.JPY" ...
## Index: Date[1:259], format: "2012-02-05" "2012-02-12" "2012-02-19" "2012-02-26" "2012-03-04" ...
## Frequency: 0.142857142857143
#Getting the first 6 rows of the exrates.zr and exrates.xts. Looking at the head of both of these objects they appear to be structured similarly.
head(exrates.zr)
## USD.EUR USD.GBP USD.CNY USD.JPY USD.EUR
## 2012-02-05 -0.39282058 -0.8523826 -0.01270912 -1.0301113 0.39282058
## 2012-02-12 -0.42063724 -0.1184583 -0.03130311 1.0571858 0.42063724
## 2012-02-19 0.44758304 0.2221127 0.06545522 1.9318720 0.44758304
## 2012-02-26 -1.40130272 -0.2407629 0.01048156 2.0452375 1.40130272
## 2012-03-04 0.02305476 -0.4546859 0.02588158 1.0197119 0.02305476
## 2012-03-11 1.19869144 0.7988383 0.22598055 0.6384155 1.19869144
## USD.GBP USD.CNY USD.JPY USD.EUR USD.GBP USD.CNY USD.JPY
## 2012-02-05 0.8523826 0.01270912 1.0301113 -1 -1 -1 -1
## 2012-02-12 0.1184583 0.03130311 1.0571858 -1 -1 -1 1
## 2012-02-19 0.2221127 0.06545522 1.9318720 1 1 1 1
## 2012-02-26 0.2407629 0.01048156 2.0452375 -1 -1 1 1
## 2012-03-04 0.4546859 0.02588158 1.0197119 1 -1 1 1
## 2012-03-11 0.7988383 0.22598055 0.6384155 1 1 1 1
head(exrates.xts)
## USD.EUR USD.GBP USD.CNY USD.JPY USD.EUR
## 2012-02-05 -0.39282058 -0.8523826 -0.01270912 -1.0301113 0.39282058
## 2012-02-12 -0.42063724 -0.1184583 -0.03130311 1.0571858 0.42063724
## 2012-02-19 0.44758304 0.2221127 0.06545522 1.9318720 0.44758304
## 2012-02-26 -1.40130272 -0.2407629 0.01048156 2.0452375 1.40130272
## 2012-03-04 0.02305476 -0.4546859 0.02588158 1.0197119 0.02305476
## 2012-03-11 1.19869144 0.7988383 0.22598055 0.6384155 1.19869144
## USD.GBP USD.CNY USD.JPY USD.EUR USD.GBP USD.CNY USD.JPY
## 2012-02-05 0.8523826 0.01270912 1.0301113 -1 -1 -1 -1
## 2012-02-12 0.1184583 0.03130311 1.0571858 -1 -1 -1 1
## 2012-02-19 0.2221127 0.06545522 1.9318720 1 1 1 1
## 2012-02-26 0.2407629 0.01048156 2.0452375 -1 -1 1 1
## 2012-03-04 0.4546859 0.02588158 1.0197119 1 -1 1 1
## 2012-03-11 0.7988383 0.22598055 0.6384155 1 1 1 1
We can plot with the ggplot2 package. In the ggplot statements we use aes, “aesthetics”, to pick x (horizontal) and y (vertical) axes. Use group =1 to ensure that all data is plotted. The added (+) geom_line is the geometrical method that builds the line plot.
#ggplot2 is a package written by Hadley Wickham and creates beautiful graphics. It is part of tidyverse. ggplot requires the following elements: the data, the aesthetics and the geometry (type of plot). ggplot can take many many more elements, but the three listed are the most basic elements that are required to make ggplot work. For more information: https://www.rstudio.com/wp-content/uploads/2015/03/ggplot2-cheatsheet.pdf
library(ggplot2)
#plotly is a package that allows for the generation of interactive graphs. One example, is that it allows you to scroll over a graph and see specific data relating to that point in the graph. From: https://plot.ly/r/time-series/
library(plotly)
##sets the title
#This is storing a character string in a vector called title.chg. This will be the title for our future graph
title.chg <- "Exchange Rate Percent Changes"
##first 4 extensible time series items
#autoplot.zoo() is able to produce a gg2plot from a zoo object. from: https://www.rdocumentation.org/packages/zoo/versions/1.8-6/topics/ggplot2.zoo
#The following line creates a graph called p1, by using autoplot.zoo which is a function that allows R to create a ggplot graph from a zoo or xts object. The first argument (exrates.xts[,1:4]) tells R to use the exrate.xts data, all the rows, but only the first 4 columns. ggtitle() is a function from ggplot2 which adds a title to the graph and ylim() sets the y axis limits.
(p1 <- autoplot.zoo(exrates.xts[,1:4]) + ggtitle(title.chg) + ylim(-5, 5))
##second 4
#The first argument tells R to use the exrates.xts data, all of its rows, but only variables 5 - 8, which contain the size data. ylim() sets the y limits.
(p2 <- autoplot.zoo(exrates.xts[,5:8]) + ggtitle("Exchange Rate Percent Changes Size") + ylim(-5, 5))
##gglotly converts ggplot2 object to plotly. Plotly is a different R package.
#ggplotly transforms the noninteractive ggplot graph to an interactive graph. It is now possible to scroll over each plot and get the index and value for each data point. The index is the date.
ggplotly(p1)
data_moments() function. Run the function using the exrates data and write a knitr::kable() report.##Auto-correlation function giving value of auto-correlation with its lagged values
#acf starts at lag 0
#coredata() is part of the zoo package. It is for time series objects and removes the index, leaving only the numeric data.From: https://www.rdocumentation.org/packages/zoo/versions/1.8-6/topics/coredata
#We are using the coredata() function and giving it the following argument, the data, which is the exrates.xts xts object all the rows and only columns 1 - 4, which are the returns. We are then running the auto-correlation function with a default lag of 0.
acf(coredata(exrates.xts[ , 1:4])) # returns
#We are repeating the same step as above, but on columns 5 - 8, the sizes.
acf(coredata(exrates.xts[ , 5:8])) # sizes
##Partial auto-correlation function, finds correlation of residuals with the next lag value. Remove variations that were already found before looking for next correlation.
#pacf starts at lag 1 and stands for partial autocorrelation. According to http://www.statsoft.com/Textbook/Time-Series-Analysis, it can provide "'cleaner' picture of serial dependencies for individual lags".
#the following code does the same as the acf code, however it runs a partial auto correlation, which has a default lag of 1.
pacf(coredata(exrates.xts[ , 1:4])) # returns
#This does the same as the line above, but with columns 5 - 8, the sizes.
pacf(coredata(exrates.xts[ , 5:8])) # sizes
# Load the data_moments() function
## data_moments function
## INPUTS: r vector
## OUTPUTS: list of scalars (mean, sd, median, skewness, kurtosis)
#Creating a function called data_moments that takes one argument. The only thing needed to use the function is what data you are using.
data_moments <- function(data){
#We need the moments package for the skewness and kurtosis functions.
library(moments)
#The matrixStats package allows you to calculate the means, medians, sd, and IQR for rows or columns in a matrix or vector.
library(matrixStats)
#This calculates the mean for each column in the data that we pass through the function and stores it in a vector called mear.r
mean.r <- colMeans(data)
#This calculates the median for each column in the data that we pass through the function and stores it in a vector called meadian.r
median.r <- colMedians(data)
#This calculates the standard deviation for each column in the data that we pass through the function and stores it in a vector called sd.r
sd.r <- colSds(data)
#This calculates the inner quartile range for each column in the data that we pass through the function and stores it in a vector called IQR.r
IQR.r <- colIQRs(data)
#This will calculate the skewness of the data inputted and save it in a vector called skewness.r - function from the moments package.
skewness.r <- skewness(data)
#this will calculate the kurtosis of the data entered and save it in a vector called kurtosis.r - function from the moments package.
kurtosis.r <- kurtosis(data)
#This is creating a data frame named result that has 6 columns, mean, median, std_dev, IQR, skewness, and kurtosis. The values for each column are calculated within the function. mean is populated with mean.r, std_dev with sd.r, median with median.r and so forth.
result <- data.frame(mean = mean.r, median = median.r, std_dev = sd.r, IQR = IQR.r, skewness = skewness.r, kurtosis = kurtosis.r)
return(result)
}
# Run data_moments()
##Run the above defined function on USD.EUR USD.GBP USD.CNY USD.JPY
#We are running the data_moments function that was written above, on the exrates.xts xts object, all the rows, but only columns 5 - 8, the sizes. We are saving the df that is outputted and naming it answer. The df answer has 6 columns and 4 rows. It contains a row for USD.EUR.size, USD.GBP.size, USD.CNY.size, and USD.JPY.size.
answer <- data_moments(exrates.xts[, 5:8])
# Build pretty table
#round() function takes 2 arguments, the data, and the amount of decimal places to round the data. In this case, we are rounding the data to 4 decimal places.
answer <- round(answer, 4)
#We are directly calling the kable function from the package knitr to create a nicely displayed table of our df answer.
knitr::kable(answer)
| mean | median | std_dev | IQR | skewness | kurtosis | |
|---|---|---|---|---|---|---|
| USD.EUR | 0.7185 | 0.5895 | 0.5499 | 0.7506 | 1.3773 | 6.3808 |
| USD.GBP | 0.6884 | 0.5601 | 0.6565 | 0.6588 | 4.0555 | 34.3779 |
| USD.CNY | 0.1700 | 0.1118 | 0.2233 | 0.1536 | 4.9157 | 41.4959 |
| USD.JPY | 0.8310 | 0.6358 | 0.7371 | 0.8352 | 1.6373 | 6.3185 |
##finds the mean in this column USD.JPY
#We are calculating the mean of the exrates.xts USD.JPY returns column
mean(exrates.xts[,4])
## [1] 0.154916
The exchange rates are not volatile, they move slightly up and down as I would expect from the market. It would be concerning if the exchange rates only moved up or moved down. This should signal a red flag for the market across the world. Though in this data set, the currencies are not synchronized and moved up and down together, each currency is impacted differently. One may move up while the other moves down. - We will calculate percentage changes as log returns of currency pairs. Our interest is in the ups and downs. To look at that we use if and else statements to define a new column called direction. We will build a data frame to house this initial analysis. Code -Using this data frame, interpret appreciation and depreciation in terms of the impact on the receipt of cash flow from customer’s accounts that are denominated in other than our USD functional currency. The receipt of cash flow for each exchange rate from the customer’s account for in terms of appreciation and depreciation will import the prices of domestic and imported goods. If the customer’s currency appreciates, the cost of domestic goods increase and the prices of imported goods decrease. Their receipt is dependent on if they are receiving payment in their local currency which would increase their cash flow if their rate has appreciated therefore, if they receive payment in a foreign currency this would reduce their cash flow as the value of that currency is now lower.
We will use the data from the first part to investigate the interactions of the distribution of exchange rates.
We want to characterize the distribution of up and down movements visually. Also we would like to repeat the analysis periodically for inclusion in management reports.
exrates.df data frame with ggplot2 and the cumulative relative frequency function stat_ecdf.##sets variable
#We are creating a numeric variable called exrates.tol.pct. This will be used to find the 95% quantile
exrates.tol.pct <- 0.95
##Produces sample quantiles using the 95% tolerance defined in function above
##We are quantiling the returns.USD.EUR column from the exrates.df data frame , our probability is .95. We are using the vector exrates.tol.pct to declare our chosen probability. The result is stored in a vector called exrates.tol
exrates.tol <- quantile(exrates.df$returns.USD.EUR, exrates.tol.pct)
##Adds label to the rounded value
#We are storing a string into variable called exrates.tol.label. The paste function takes at least one argument and then concatenates the vectors into a type chr. In the line below, we are using the paste function to concatenate a statement that states what the tolerable rate is. We are entering "Tolerable Rate = ", and the second argument in the value stored in the exrates.tol vector rounded to the nearest hundreth. Because we added a space after the equal sign and inside the quotations we are telling R that the two arguments are separated by "", which means nothing is needed to separate them. If we did not put a space after the equal sign, then we would need to use the argument sep = " ", with a space between the quotation marks.
exrates.tol.label <- paste("Tolerable Rate = ", round(exrates.tol, 2), "%", sep = "")
##Plot shows that the Tolerable Rate is 1.47%
##We are using ggplot to create an alternative to a density graph to visualize distribution. stat_ecdf creates an empirical cumulative distribution. We are using the exrates.df data frame and the aesthetics are x-axis is the returns.USD.EUR column from the exrates.df. We are specifying the fill to be based on the direction.USD.EUR column. The stat_ecdf function is creates a scatter plot, that is colored blue. geom_vline is used to create a x-intercept at the quantile value where 95% of the data is less than the value. This line is going to be red, which is shown in color = "red" andthe size of the line is set at 1.5. We are adding an annotation on the graph using the annotate function. The following arguments are in the annotate function. The type of annotation, in this case it is text, the coordinates for the text (x, y), the text string to include and the color of the text. x = exrates.tol + 1, is telling R to place the text where x = 2.47 y = .75 tells R to place the text where y = .75. The coordinates for the text are (2.47, .75) on the graph. label = exrates.tol.label, is telling R to use the chr string stored in exrates.tol.label for the text. color = "darkred" tells R that we want the text to be in dark red.
#For more information on the stat_ecdf function: https://www.rdocumentation.org/packages/ggplot2/versions/3.2.0/topics/stat_ecdf
#For more information on the annotate function: https://www.rdocumentation.org/packages/ggplot2/versions/3.2.0/topics/annotate
p <- ggplot(exrates.df, aes(returns.USD.EUR, fill = direction.USD.EUR)) + stat_ecdf(colour = "blue", size = 0.75, geom = "point") + geom_vline(xintercept = exrates.tol, colour = "red", size = 1.5) + annotate("text", x = exrates.tol + 1 , y = 0.75, label = exrates.tol.label, colour = "darkred") + ggtitle("Empirical Cumulative Distribution of Returns for USD.EUR")+ xlab("Return") + ylab("Probability")
#makes our graph defined above interactive. It is now possible to scroll over every point on the scatter plot and find out x and y value.
ggplotly(p)
# Part 2 Question 1: ## 1. How can we show the shape of our exposure to euros, especially given our tolerance for risk? Suppose corporate policy set tolerance at 95%. Let’s use the exrates.df data frame with ggplot2 and the cumulative relative frequency function stat_ecdf.
We show an empirical cumulative distribution of returns to asses our tolerance at 95%, this means that there’s a 5% chance that the exchange rate will increase. Based on our analysis, we have calculated a maximum tolerable rate of 1.47%.
corr_rolling, and embed this function into the rollapply() function (look this one up!).library(psych)
vis <- exrates.df[c("returns.USD.EUR", "returns.USD.GBP","returns.USD.CNY", "returns.USD.JPY")]
if(!is.matrix(vis)) vis <- rbind(vis, deparse.level = 0L)
pairs.panels(vis)
Historically, there is a stronger correlation between USD.EUR and USD.GBP as well as EUR and JPY. There is barely a correlation between USD.GBP and USD.JPY, and there are only weak correlations between the other currencies. With regards to our business and business decisions, we should expect our EUR and GPB deals to impact one another, but we don’t need to worry as much about our deals in GBP impacting the JPY market.
#ts() is a function that changes numeric vectors to a time series object
#We are converting the returns.USD.EUR column from the exrates.df df into a time series object and saving it as time series object named one.
one <- ts(exrates.df$returns.USD.EUR)
#We are converting the returns.USD.GBP column from the exrates.df df into a time series object and saving it as time series object named two
two <- ts(exrates.df$returns.USD.GBP)
# or
#Our attempt: #cross-correlations how one market is related to the other in cross terms, we can see that there is a little bit of a relationship at lags -3, -1, 1, and possibly 17.
ccf(one, two, main = "Returns GBP vs. Eur", lag.max = 20, xlab = "lag", ylab = "correlation", ci.col = "red")
There seems to be a cyclical pattern throughout this data. We can see that there is a little relationship at lags -3, -1, 1, and possibly 17.
#This is taking the first vector from the zoo reg series, which is the same information that is contained in the exrates.df$returns.USD.EUR and converting it to a time series object named one
#one <- ts(exrates.zr[,1])
#This is taking the second vector from the zoo reg series, which is the same information that is contained in the exrates.df$returns.USD.GBP and converting it to a time series object named two
#two <- ts(exrates.zr[,2])
#taking the absolute value and appling the ccf function. If the absolute value of the returns are over the red dotted line, it will give us an idea of how volatile the correlations are. However, we are very confused on why this is returning a couple of instances that show a negative value.
# build function to repeat these routines
#This creates a function called run_ccf it takes the following arguments: two series, a title, lag which is defaulted to 20 and color which is defaulted to red.
run_ccf <- function(one, two, main = "one vs. two", lag = 20, color = "red"){
# one and two are equal length series
# main is title
# lag is number of lags in cross-correlation
# color is color of dashed confidence interval bounds
#This is a fail safe that will stop the function from running if the length of series one does not equal the length of series 2. Basically, if they have a different number of entries then the function will not run
stopifnot(length(one) == length(two))
#This converts object one into a time series
one <- ts(one)
#This converts object 2 into a time series
two <- ts(two)
#The title is saved in a character string called main, you can directly name the title by adding main = "title" in the inputs for the function
main <- main
#This sets the lag to whatever you set the lag equals to in the input of the function. If left blank the lag will automatically default to 20.
lag <- lag
#This sets the color to whatever you set the color equals to in the input of the function. If left blank the color will automatically defualt to red.
color <- color
#This runs the ccf function on the data you inputed, it sets the x and y axis labels to nothing.
ccf(one, two, main = main, lag.max = lag, xlab = "", ylab = "", ci.col = color)
#end run_ccf
}
##creates time series on the EUR returns data
one <- ts(exrates.df$returns.USD.EUR)
##creates time series on GBP returns data
two <- ts(exrates.df$returns.USD.GBP)
# or
##alternate way of setting up above time series
one <- exrates.zr[,1]
two <- exrates.zr[,2]
title <- "EUR vs. GBP"
##ccf computes sample crosscorrelation function of the absolute value of one and two, up to lag 20. Relationship is shown when the lines go over the red dashed horizontal lines.
#Looking at absolute values gives a notion of how volatile the correlations are. If the lines go over the red dashed line then it shows a preponderance of movement between the two. If one company is volatile then the other country will be volatile. If one country was volatable a long time ago then the other country will be volatile now and in the future.
run_ccf(abs(one), abs(two), main = "Absolute Value of Returns USD.EUR and USD.GBP", lag = 20, color = "red")
# now for volatility (sizes)
#To check for volatility we take the absolute value of returns, which is actually what we had already calculated in the sizes columns. Therefore, we are using column 5 and 6 instead of taking the absolute values of columns 1 and 2. This was in an attempt to not show any negative data points, since it is the absolute value. However, this produced the same graph as before.
one <- ts(exrates.zr[,5])
two <- ts(exrates.zr[,6])
##sets the title for plot
title <- "EUR vs. GBP: volatility"
##uses the above defined function on the absolute value of time series data
run_ccf(one, two, main = title, lag = 20, color = "red")
# We see some small raw correlations across time with raw returns. More revealing, we see volatility of correlation clustering using return sizes.
We can see that by looking at the absolute auto correlation at cross-terms there is a little bit of a relationship at lag -18, and 18, which show volatility at those points.
One more experiment, rolling correlations and volatilities using these functions:
#Creating a corr_rolling function that takes an data input
corr_rolling <- function(x) {
#dim is calculating the number of columns of the data that was inputed into the function
dim <- ncol(x)
#We are creating a correlation matrix of the data inputed by using cor(x) and then [lower.tri(diag(dim), diag = FALSE)] to get the lower part of our correlation matrix from: http://www.sthda.com/english/wiki/elegant-correlation-table-using-xtable-r-package
corr_r <- cor(x)[lower.tri(diag(dim), diag = FALSE)]
#the function returns the result that is stored in corr_r
return(corr_r)
}
#Creating a new function called vol_rolling
vol_rolling <- function(x){
library(matrixStats)
#We are getting the standard deviation for each column in the data passed through the function
vol_r <- colSds(x)
#The function returns the standard deviation for each column in the data passed through the function that was stored in vol_r
return(vol_r)
}
#This is storing the first 4 columns of the exrates.xts xts object in an new xts object called ALL.r,
ALL.r <- exrates.xts[, 1:4]
#we are storing 90 in a numeric vector named window
window <- 90 #reactive({input$window})
#rollapply() is a function from the zoo package. It takes the following arguments: the data, a series of observations, the width which according to rdocumentation is the "numbers of observations which is aligned to the original sample according to the alight argument" the function to be applied, align which says how the index should be aligned either left, right or center. Since by.column = False the function is not separately applied to each column, but instead the calculations are done on mutiple columns. It calculates the moving average. From: https://www.rdocumentation.org/packages/zoo/versions/1.8-6/topics/rollapply and https://rpubs.com/simaan84/rollapply
corr_r <- rollapply(ALL.r, width = window, corr_rolling, align = "right", by.column = FALSE)
#Changing the column names because the function calculated the moving average between every set of columns in the initial xts object
colnames(corr_r) <- c("EUR.GBP", "EUR.CNY", "EUR.JPY", "GBP.CNY", "GBP.JPY", "CNY.JPY")
#Using rollapply() to calculate the vol_rolling moving averages for each column in the all.r xts object, the arguments are the same as the example above, but the function changed. Even though the by.column = False this function is not applied to all of the combination of 2 columns, because the function itself specifically calculates the colSds. Instead it calculates a moving average for each entry in the columns
vol_r <- rollapply(ALL.r, width = window, vol_rolling, align = "right", by.column = FALSE)
#Changing the column names
colnames(vol_r) <- c("EUR.vol", "GBP.vol", "CNY.vol", "JPY.vol")
#Getting the year from the dates in the index (row names) of the coor_r xts object.
year <- format(index(corr_r), "%Y")
#Merging the all.r, corr_r, vol_r and year xts objects (year is a vector of equal length) into one big xts object called r_corr_vol
r_corr_vol <- merge(ALL.r, corr_r, vol_r, year)
exrate data to understand how correlations and volatilities depend upon one another.Correlations and volatilites are related and yes, we do have to be concerned that inter-market transactions will impact transactions in a single market. If there is a large purchase or event in one market, the other markets will be impacted. As volitility rises, the amount of dispersion of correlation also grows. In short – relationships between high-stress periods and correlation are abundant
#Is a quantile regression package that provides "estimation and inference methods for models of conditional quantiles" from: https://www.rdocumentation.org/packages/quantreg/versions/5.42.1 We are going to use the rq function from this package
library(quantreg)
#Creating a vector using the seq() function we are telling R to start at 0.05 and stop when it reaches 0.95 and to count by 0.05.
taus <- seq(.05,.95, .05) # Roger Koenker UIC Bob Hogg and Allen Craig
#we are creating a quantile regression of the log of CNY.JPY which contains the moving average that was calculated with the corr_rolling function (the dependent variable or y) and the log if JPY.vol which was calculated using the rollapply and the vol_rolling function as the independent variable (x). tau states the quantile to use, in this case we are passing through the taus vector so we are calculating the quantile regression for 19 quantiles and we are using the data r_corr_col. From: https://www.rdocumentation.org/packages/quantreg/versions/2.0-2/topics/rq Taus allows us to create upper and lower bounds between the relationship of correlation and volatility
fit.rq.CNY.JPY <- rq(log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
#We receive an error because we do have NAs in our CNY.JPY and JPY.vol data.
#lm() creates a simple regression model or linear model. We have the same dependent and independent variables, but in this case we do not have all of the quanitles to run the regression on as we did with rq().
fit.lm.CNY.JPY <- lm(log(CNY.JPY) ~ log(JPY.vol), data = r_corr_vol)
# Some test statements
#We are looking at the summary for each quantile regression run by the rq() function. se = boot is a method to compute the standard error. The summary() function returns information for each quantile regression that was run. It returns the call (or formula), the tau (quantile) and the coefficients the x and y intercept the standard error, t-value and pr(>|t|)
(CNY.JPY.summary <- summary(fit.rq.CNY.JPY, se = "boot"))
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.05
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -4.21770 0.23246 -18.14351 0.00000
## log(JPY.vol) -5.54144 2.02108 -2.74182 0.00681
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.1
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -3.96984 0.13525 -29.35273 0.00000
## log(JPY.vol) -4.05415 1.27333 -3.18388 0.00175
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.15
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -3.65544 0.14267 -25.62154 0.00000
## log(JPY.vol) -5.04776 1.35294 -3.73095 0.00027
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.2
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -3.41946 0.13493 -25.34190 0.00000
## log(JPY.vol) -3.90841 1.32089 -2.95893 0.00356
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.25
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -3.24424 0.14784 -21.94397 0.00000
## log(JPY.vol) -2.70119 1.43090 -1.88775 0.06088
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.3
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -3.00646 0.13886 -21.65060 0.00000
## log(JPY.vol) -1.16454 1.23468 -0.94319 0.34701
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.35
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -2.89360 0.07796 -37.11774 0.00000
## log(JPY.vol) -0.68706 0.80881 -0.84947 0.39690
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.4
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -2.85405 0.05942 -48.03034 0.00000
## log(JPY.vol) -0.52545 0.99802 -0.52649 0.59928
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.45
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -2.78600 0.07307 -38.12678 0.00000
## log(JPY.vol) -0.88196 1.08166 -0.81538 0.41608
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.5
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -2.72762 0.10209 -26.71880 0.00000
## log(JPY.vol) -0.67233 1.59331 -0.42197 0.67362
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.55
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -2.53524 0.11488 -22.06830 0.00000
## log(JPY.vol) -1.64576 1.76426 -0.93284 0.35232
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.6
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -2.43784 0.13196 -18.47378 0.00000
## log(JPY.vol) -1.85451 1.75957 -1.05396 0.29350
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.65
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -2.19872 0.13686 -16.06550 0.00000
## log(JPY.vol) -2.84742 1.59857 -1.78123 0.07678
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.7
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -2.14073 0.11226 -19.06906 0.00000
## log(JPY.vol) -2.65416 0.98247 -2.70150 0.00765
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.75
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -1.97726 0.14500 -13.63618 0.00000
## log(JPY.vol) -1.89488 0.97644 -1.94061 0.05407
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.8
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -1.75658 0.14344 -12.24627 0.00000
## log(JPY.vol) -0.67468 0.94524 -0.71376 0.47642
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.85
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -1.61049 0.08522 -18.89804 0.00000
## log(JPY.vol) 0.06474 0.54978 0.11776 0.90641
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.9
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -1.57462 0.03799 -41.44893 0.00000
## log(JPY.vol) 0.06146 0.22003 0.27933 0.78035
##
## Call: rq(formula = log(CNY.JPY) ~ log(JPY.vol), tau = taus, data = r_corr_vol)
##
## tau: [1] 0.95
##
## Coefficients:
## Value Std. Error t value Pr(>|t|)
## (Intercept) -1.45763 0.07225 -20.17453 0.00000
## log(JPY.vol) 0.45428 0.51501 0.88208 0.37906
#plotting the summary of the fit to see how the parameters (the relationship between correlation and volatility move over time)
plot(CNY.JPY.summary)
#
#To better understand what this means look at 4.3.1 in the book. Section title On to quantiles. Also async 3.4 Exercise Getting Caught in the Crosscurrent
# NOTES: It looks like there is a not a giant difference between the upper and lower bound of the patterns of the relationship between correlations and volatility. There is a very strong pattern.
Here is the quantile regression part of the package.
taus as the quantiles of interest.quantreg package and a call to the rq function.library(quantreg)
library(magick)
img <- image_graph(res = 96)
datalist <- split(r_corr_vol, r_corr_vol$year)
out <- lapply(datalist, function(data){
p <- ggplot(data, aes(JPY.vol, CNY.JPY)) +
geom_point() +
ggtitle(data$year) +
geom_quantile(quantiles = c(0.05, 0.95)) +
geom_quantile(quantiles = 0.5, linetype = "longdash") +
geom_density_2d(colour = "red")
print(p)
})
while (!is.null(dev.list())) dev.off()
#img <- image_background(image_trim(img), 'white')
animation <- image_animate(img, fps = .5)
animation
Attempt interpretations to help managers understand the way market interactions affect accounts receivables.
In the ccf() function we get results that produce positive and negative lags. A positive lag looks back and a negative lag (a lead) looks forward in the history of a time series. Leading and lagging two different serries, then computing the moments and corelations show a definite asymmetry.
Suppose we lead the USD.EUR return by 5 days and lag the USD.GBP by 5 days. We will compare the correlation in this case with the opposite: lead the USD.GBP return by 5 days and lag the USD.EUR by 5 days. We will use the dplyr package to help us.
#The dplyr library is a grammar of data manipulation that provides a consistent set of verbs for common data manipulation tasks. From: https://dplyr.tidyverse.org/
library(dplyr)
#Stores USD.EUR as a numeric variable
x <- as.numeric(exrates.df$returns.USD.EUR) # USD.EUR
#Stores USD.GBP as a numeric variable
y <- as.numeric(exrates.df$returns.USD.GBP) # USD.GBP
#A dataframe with missing values removed is created, with the USD.GBP lagged by 5 days, and USD.EUR lead by 5 days. This is used for comparing values ahead or behind the current values
xy.df <- na.omit(data.frame(date = dates, ahead_x= lead(x, 5), behind_y = lag(y, 5)))
#A dataframe with missing values removed is created, with the USD.EUR lagged by 5 days, and USD.GBP lead by 5 days
yx.df <- na.omit(data.frame(date = dates, ahead_y =lead(y, 5), behind_x = lag(x, 5)))
#The data_moments function defined earlier is used on columns 2 and 3, representing lagged USD.GBP and lead USD.EUR. Missing values are omitted.
answer <- data_moments(na.omit(as.matrix(xy.df[,2:3])))
#The answer is rounded to 4 decimal places
answer <- round(answer, 4)
#We are calling the kable function from the package knitr to create a table displaying the dataframe
knitr::kable(answer)
| mean | median | std_dev | IQR | skewness | kurtosis | |
|---|---|---|---|---|---|---|
| ahead_x | 0.0872 | 0.1078 | 0.905 | 1.1820 | 0.1671 | 3.6297 |
| behind_y | 0.0945 | -0.0130 | 0.951 | 1.1134 | 1.7055 | 13.2014 |
#The data_moments function defined earlier is used on columns 2 and 3, representing lagged USD.EUR and lead USD.GBP. Missing values are omitted.
answer <- data_moments(na.omit(as.matrix(yx.df[,2:3])))
#The answer is rounded to 4 decimal places
answer <- round(answer, 4)
#We are calling the kable function from the package knitr to create a table displaying the dataframe
knitr::kable(answer)
| mean | median | std_dev | IQR | skewness | kurtosis | |
|---|---|---|---|---|---|---|
| ahead_y | 0.1072 | -0.0047 | 0.9593 | 1.1139 | 1.6409 | 12.7057 |
| behind_x | 0.0673 | 0.1043 | 0.8953 | 1.1624 | 0.1285 | 3.5876 |
#Calculates the correlation between the lagged USD.GBP and lead USD.EUR
cor(as.numeric(xy.df$ahead_x), as.numeric(xy.df$behind_y))
## [1] 0.0003739413
#Calculates the correlation between the lagged USD.EUR and lead USD.GBP
cor(as.numeric(yx.df$ahead_y), as.numeric(yx.df$behind_x))
## [1] -0.004339494
Leading x, lagging y will produce a negative correlation. The opposite produces an even smaller and positive correlation. Differences in means, etc. are not huge between the two cases, but when combined produce the correlational differences.
In summation, more analysis (and more data!) is needed. This is a very good start to a much longer story. We have insights into larger trends and clear areas where we need both more data (complete yearly data, for one) and more thorough analysis (comparing all the currencies in the last section instead of just JPY and CNY).